Plotly: Trading charts examples

import pandas as pd
import numpy as np
from datetime import datetime, timedelta

import plotly.graph_objects as go
from plotly.subplots import make_subplots

1 Chart 2 panes: Bars + Volume

# Generate sample data
n = 500
np.random.seed(42)
dates = pd.date_range(start='2024-01-01', periods=n, freq='D')
opens = np.random.normal(100, 2, n)
closes = opens + np.random.normal(0, 2, n)
highs = np.maximum(opens, closes) + np.random.uniform(0, 1, n)
lows = np.minimum(opens, closes) - np.random.uniform(0, 1, n)
volumes = np.random.uniform(1000, 5000, n)

# Create DataFrame
df = pd.DataFrame({
    'Date': dates,
    'Open': opens,
    'High': highs,
    'Low': lows,
    'Close': closes,
    'Volume': volumes
})

df.head()
Date Open High Low Close Volume
0 2024-01-01 100.993428 103.013266 100.277105 102.845783 1876.275236
1 2024-01-02 99.723471 103.646873 99.651387 103.542305 1146.885450
2 2024-01-03 101.295377 101.931807 98.426985 98.498242 1432.103017
3 2024-01-04 103.046060 104.878474 103.033951 104.171998 2355.442597
4 2024-01-05 99.531693 99.563279 97.273907 98.230408 4210.342718
# Create the figure with secondary y-axis
fig = make_subplots(rows=2, cols=1, 
                    shared_xaxes=True, 
                    vertical_spacing=0.03, 
                    row_heights=[0.7, 0.3])

# Add candlestick chart
fig.add_trace(go.Candlestick(
    x=df['Date'],
    open=df['Open'],
    high=df['High'],
    low=df['Low'],
    close=df['Close'],
    name='OHLC'
), row=1, col=1)

# Add volume bar chart
fig.add_trace(go.Bar(
    x=df['Date'],
    y=df['Volume'],
    name='Volume'
), row=2, col=1)

# Update layout
fig.update_layout(
    title='Candlestick Chart with Volume',
    yaxis_title='Price',
    yaxis2_title='Volume',
    height=800,
    showlegend=True,
    # Move rangeslider to bottom
    xaxis2=dict(
        rangeslider=dict(
            visible=True,
            thickness=0.05
        )
    ),
    # Hide rangeslider for top chart
    xaxis=dict(
        rangeslider=dict(
            visible=False
        )
    )
)

# Update y-axes ranges to remove auto-scaling
fig.update_yaxes(fixedrange=False)  # Allow zooming on y-axis

# Show the plot
fig.show()

2 Chart 3 panes: Bars with SMA(20) + Volume + SMA(50)

# Generate sample data
np.random.seed(42)
dates = pd.date_range(start='2024-01-01', periods=100, freq='D')  # Increased to 100 days
opens = np.random.normal(100, 2, 100)
closes = opens + np.random.normal(0, 2, 100)
highs = np.maximum(opens, closes) + np.random.uniform(0, 1, 100)
lows = np.minimum(opens, closes) - np.random.uniform(0, 1, 100)
volumes = np.random.uniform(1000, 5000, 100)

# Create DataFrame
df = pd.DataFrame({
    'Date': dates,
    'Open': opens,
    'High': highs,
    'Low': lows,
    'Close': closes,
    'Volume': volumes
})

# Calculate SMAs
df['SMA20'] = df['Close'].rolling(window=20).mean()
df['SMA50'] = df['Close'].rolling(window=50).mean()

# Preview
df.tail()
Date Open High Low Close Volume SMA20 SMA50
95 2024-04-05 97.072970 98.419079 96.954152 97.843605 3481.238205 99.865527 100.160386
96 2024-04-06 100.592241 100.980410 98.706999 98.824526 3816.319072 99.796748 100.204780
97 2024-04-07 100.522111 101.472849 99.872900 100.829561 1851.856646 99.722774 100.231905
98 2024-04-08 100.010227 100.584897 99.264182 100.126644 1545.485902 99.746396 100.199815
99 2024-04-09 99.530826 100.076443 96.661516 97.244885 1058.178663 99.535380 100.203355
# Create the figure with three subplots
fig = make_subplots(rows=3, cols=1, 
                    shared_xaxes=True, 
                    vertical_spacing=0.03, 
                    row_heights=[0.5, 0.25, 0.25])

# Add candlestick chart
fig.add_trace(go.Candlestick(
    x=df['Date'],
    open=df['Open'],
    high=df['High'],
    low=df['Low'],
    close=df['Close'],
    name='OHLC'
), row=1, col=1)

# Add SMA20 to price chart
fig.add_trace(go.Scatter(
    x=df['Date'],
    y=df['SMA20'],
    line=dict(color='orange', width=2),
    name='SMA(20)'
), row=1, col=1)

# Add volume bar chart
fig.add_trace(go.Bar(
    x=df['Date'],
    y=df['Volume'],
    name='Volume'
), row=2, col=1)

# Add SMA50 in separate pane
fig.add_trace(go.Scatter(
    x=df['Date'],
    y=df['SMA50'],
    line=dict(color='blue', width=2),
    name='SMA(50)'
), row=3, col=1)

# Update layout
fig.update_layout(
    title='Candlestick Chart with Volume and SMAs',
    yaxis_title='Price',
    yaxis2_title='Volume',
    yaxis3_title='SMA(50)',
    height=900,  # Increased height to accommodate new pane
    showlegend=True,
    # Move rangeslider to bottom (now on xaxis3)
    xaxis3=dict(
        rangeslider=dict(
            visible=True,
            thickness=0.05
        )
    ),
    # Hide rangeslider for other charts
    xaxis=dict(rangeslider=dict(visible=False)),
    xaxis2=dict(rangeslider=dict(visible=False))
)

# Update y-axes ranges to remove auto-scaling
fig.update_yaxes(fixedrange=False)  # Allow zooming on y-axis

# Show the plot
fig.show()

3 Chart: 1/5/15-min timeframes

# Generate 1-minute data
def generate_1min_data(n_periods=480*2):  # 16 hours of 1-min data
    np.random.seed(42)
    
    # Generate initial close prices using random walk
    returns = np.random.normal(0, 0.0003, n_periods)
    close_prices = 100 * np.exp(np.cumsum(returns))
    
    # Initialize data storage
    data = []
    current_time = datetime(2024, 1, 1, 9, 30)  # Start at market open
    current_price = close_prices[0]
    
    for i in range(n_periods):
        # Generate realistic OHLC based on close price
        close = close_prices[i]
        open_price = current_price  # Previous close becomes next open
        high = max(open_price, close) * (1 + abs(np.random.normal(0, 0.0002)))
        low = min(open_price, close) * (1 - abs(np.random.normal(0, 0.0002)))
        
        data.append({
            'time': current_time,
            'open': open_price,
            'high': high,
            'low': low,
            'close': close
        })
        
        current_price = close
        current_time += timedelta(minutes=1)
    
    return pd.DataFrame(data)

# Aggregate data to higher timeframes
def aggregate_ohlc(df, minutes):
    df_resampled = df.set_index('time')
    agg_dict = {
        'open': 'first',
        'high': 'max',
        'low': 'min',
        'close': 'last'
    }
    aggregated = df_resampled.resample(f'{minutes}min').agg(agg_dict).reset_index()
    return aggregated
# Generate data for 3 timeframes
df_1min = generate_1min_data()
df_5min = aggregate_ohlc(df_1min, 5)
df_15min = aggregate_ohlc(df_1min, 15)

# Preview
df_1min.head()
time open high low close
0 2024-01-01 09:30:00 100.014903 100.027759 99.988316 100.014903
1 2024-01-01 09:31:00 100.014903 100.018834 99.996572 100.010754
2 2024-01-01 09:32:00 100.010754 100.031984 99.981949 100.030189
3 2024-01-01 09:33:00 100.030189 100.089442 99.994159 100.075904
4 2024-01-01 09:34:00 100.075904 100.076708 100.040239 100.068874
# Create subplot figure
fig = make_subplots(
    rows=3, cols=1,
    shared_xaxes=True,
    vertical_spacing=0.03,
    subplot_titles=('1-minute', '5-minute', '15-minute'),
    row_heights=[0.4, 0.3, 0.3]
)

# Add candlesticks for each timeframe
timeframes = [(df_1min, 1), (df_5min, 2), (df_15min, 3)]

for df, row in timeframes:
    fig.add_trace(
        go.Candlestick(
            x=df['time'],
            open=df['open'],
            high=df['high'],
            low=df['low'],
            close=df['close'],
            name=f'{row*1 if row==1 else row*5 if row==2 else 15}-min'
        ),
        row=row, col=1
    )

# Update layout
fig.update_layout(
    height=900,
    title='Multi-timeframe Candlestick Charts',
    showlegend=True,
    xaxis=dict(rangeslider=dict(visible=False)),
    xaxis2=dict(rangeslider=dict(visible=False)),
    xaxis3=dict(rangeslider=dict(visible=True, thickness=0.05))
)

# Allow y-axis zooming
fig.update_yaxes(fixedrange=False)

fig.show()

4 Chart: 1/5/15-min timeframes + EMA(20/50/100/200)

# Generate data for all timeframes
df_1min = generate_1min_data()
df_5min = aggregate_ohlc(df_1min, 5)
df_15min = aggregate_ohlc(df_1min, 15)

# Calculate EMAs for all timeframes
for df in [df_1min, df_5min, df_15min]:
    df['EMA20'] = df['close'].ewm(span=20, adjust=False).mean()
    df['EMA50'] = df['close'].ewm(span=50, adjust=False).mean()
    df['EMA100'] = df['close'].ewm(span=100, adjust=False).mean()
    df['EMA200'] = df['close'].ewm(span=200, adjust=False).mean()

# Preview
df_1min.tail()
time open high low close EMA20 EMA50 EMA100 EMA200
955 2024-01-02 01:25:00 100.580702 100.627246 100.571481 100.614421 100.597503 100.570004 100.439655 100.247045
956 2024-01-02 01:26:00 100.614421 100.666768 100.602657 100.624767 100.600099 100.572151 100.443320 100.250804
957 2024-01-02 01:27:00 100.624767 100.639181 100.606459 100.638556 100.603762 100.574756 100.447186 100.254662
958 2024-01-02 01:28:00 100.638556 100.674612 100.625115 100.655759 100.608714 100.577932 100.451316 100.258653
959 2024-01-02 01:29:00 100.655759 100.675163 100.651988 100.669280 100.614482 100.581514 100.455633 100.262739
# Create subplot figure
fig = make_subplots(
    rows=3, cols=1,
    shared_xaxes=True,
    vertical_spacing=0.03,
    subplot_titles=('1-minute', '5-minute', '15-minute'),
    row_heights=[0.4, 0.3, 0.3]
)

# EMA configuration for consistent colors and names
ema_configs = [
    ('EMA20', 'darkgreen', 'EMA(20)'),
    ('EMA50', 'darkblue', 'EMA(50)'),
    ('EMA100', 'purple', 'EMA(100)'),
    ('EMA200', 'darkred', 'EMA(200)')
]

# Function to add candlestick and EMAs for a timeframe
def add_timeframe_plots(df, row, candlestick_name):
    # Add candlestick
    fig.add_trace(
        go.Candlestick(
            x=df['time'],
            open=df['open'],
            high=df['high'],
            low=df['low'],
            close=df['close'],
            name=candlestick_name
        ),
        row=row, col=1
    )
    
    # Add EMAs
    for col, color, name in ema_configs:
        fig.add_trace(
            go.Scatter(
                x=df['time'],
                y=df[col],
                line=dict(color=color, width=1),
                name=f"{name} ({candlestick_name})",
                showlegend=True
            ),
            row=row, col=1
        )

# Add plots for each timeframe
add_timeframe_plots(df_1min, 1, '1-min')
add_timeframe_plots(df_5min, 2, '5-min')
add_timeframe_plots(df_15min, 3, '15-min')

# Update layout
fig.update_layout(
    height=900,
    title='Multi-timeframe Candlestick Charts with EMAs',
    showlegend=True,
    xaxis=dict(rangeslider=dict(visible=False)),
    xaxis2=dict(rangeslider=dict(visible=False)),
    xaxis3=dict(rangeslider=dict(visible=True, thickness=0.05))
)

# Allow y-axis zooming
fig.update_yaxes(fixedrange=False)

fig.show()